#include <memory.h>


FM_LIST  FM_LIST_HEAD;              // 空闲定长内存管理单元链表头

/******************************************************************************
* 函数名称: E_RET FixedMemInit(U08* mem, U32 size)
* 功能说明: 定长内存管理初始化
* 输入参数: U08* mem        --定长内存基址
    　　　　U32 size        --定长内存长度
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 以管理单元为节点，将管理单元组织成单向链表
******************************************************************************/
E_RET FixedMemInit(U08* mem, U32 size)
{
    U32 max = 0;
    U32 i = 0;
    FM_NODE* current = NULL;    // 当前管理单元节点
    FM_NODE* next = NULL;       // 下一个管理单元节点

    // 检查参数合法性
    if(NULL == mem || size < (FM_NODE_SIZE + FM_ALLOC_SIZE))
        return E_ERR;

    // 计算一块内存最多可以分成多少个内存单元（1内存单元 = 1管理单元+1分配单元）
    max = size / (FM_NODE_SIZE + FM_ALLOC_SIZE);
    
    // 初始化定长内存链表，以管理单元为节点，将所有的管理单元组织成一个单向链表
    FM_LIST_HEAD.max    = max;
    FM_LIST_HEAD.nbase  = (FM_NODE *)mem;                               // 管理单元基址
    FM_LIST_HEAD.ubase  = (FM_UNIT *)((U32)mem + max*FM_NODE_SIZE);     // 分配单元基址
    FM_LIST_HEAD.node   = (FM_NODE *)mem;
    for(i = 0; i < max - 1; i++)
    {
        // 当前节点（管理单元）的 next 指针指向下一个节点（管理单元）
        current = FM_LIST_HEAD.node + i;
        next = FM_LIST_HEAD.node + i + 1;
        current->next = next;
    }

    // 最后一个节点（管理单元）的 next 指针指向 NULL
    next->next = NULL;

    return E_OK;
}

/******************************************************************************
* 函数名称: void* FixedMemAlloc(void)
* 功能说明: 申请定长内存
* 输入参数: 无
* 输出参数: 无
* 函数返回: void*           --申请到的分配单元地址
* 其它说明: FM_LIST_HEAD 链表中所有节点都是可用的
           申请时只要将第一个节点所对应的分配内存地址返回，并将该节点从链表中删除即可
******************************************************************************/
void* FixedMemAlloc(void)
{
    void* alloc = NULL;
    FM_NODE* node = NULL;
    U32 index = 0;

    // 如果头结点为空，表明已经没有空闲的内存可分配了
    if(NULL == FM_LIST_HEAD.node)
        return NULL;
    
    // 取空闲定长内存管理单元链表第一个节点
    node = FM_LIST_HEAD.node;
    
    // 计算出是第几个管理单元，其在管理单元中的偏移个数就等同于在分配单元中的偏移个数
    // 根据这个偏移量就能算出对应的分配单元地址，最后再将这个分配单元地址返回
    index = (U32)(node - FM_LIST_HEAD.nbase);

    alloc = FM_LIST_HEAD.ubase + index;

    // 从链表头删除节点，即将 FM_LIST_HEAD 的头结点指向下一个节点
    FM_LIST_HEAD.node = node->next;

    // 用于释放内存时校验
    node->unit = alloc;

    return alloc;
}

/******************************************************************************
* 函数名称: E_RET FixedMemFree(void* ptr)
* 功能说明: 释放定长内存
* 输入参数: void* ptr       --要释放的分配单元地址
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 根据传入的分配单元地址 ptr，计算出对应的管理单元，将其加入到 FM_LIST_HEAD 链表中
******************************************************************************/
E_RET FixedMemFree(void* ptr)
{
    U32 index = 0;
    FM_NODE* node = NULL;

    // 检查参数合法性
    if(NULL == ptr)
        return E_ERR;

    // 计算出是第几个分配单元，其在分配单元中的偏移个数就等同于在管理单元中的偏移个数
    // 根据这个偏移值就能算出对应的管理单元地址，最后再将这个管理单元插入链表头
    index = (U32)((FM_UNIT *)ptr - FM_LIST_HEAD.ubase);
    // 合法性检查
    if(index >= FM_LIST_HEAD.max)
        return E_ERR;
    node = FM_LIST_HEAD.nbase + index;
    // node->unit 在 FixedMemAlloc 被赋值，只有当申请内存地址与释放内存地址相同是才合法
    if(node->unit != ptr)
        return E_ERR;

    // 将节点插入链表头
    node->next = FM_LIST_HEAD.node;
    FM_LIST_HEAD.node = node;

    return E_OK;
}

